#pragma once

#include <iostream>
#include <string>
#include <mutex>
#include <functional>
namespace mystl
{
    template <typename T>
    using shared_deleter = std::function<void(T *)>;
    template <class T>
    class shared_ptr
    {
    private:
        T *_data;
        std::mutex _mtx;
        int *_count;
        shared_deleter<T> _deleter;
        static void default_deleter(T *data)
        {
            if (data)
                delete data;
        }

    public:
        explicit shared_ptr(T *data = nullptr, const shared_deleter<T> &deleter = std::bind(&shared_ptr::default_deleter, std::placeholders::_1))
            : _data(data),
              _deleter(deleter)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            if (_data == nullptr)
            {
                _count = new int(0);
            }
            else
            {
                _count = new int(1);
            }
        }
        explicit shared_ptr(const shared_ptr<T> &other)
            : _data(other._data),
              _count(other._count)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            if (_data != nullptr)
            {
                std::cout << "计数 : " << *_count << std::endl;
                (*_count)++;
            }
        }
        T &operator*() const
        {
            std::unique_lock<std::mutex> lock(_mtx);
            if (_data == nullptr)
            {
                throw "data为空!";
            }
            return *_data;
        }
        ~shared_ptr()
        {
            std::unique_lock<std::mutex> lock(_mtx);
            std::cout << "计数 : " << *_count << std::endl;
            (*_count)--;
            if (*_count == 0)
            {
                delete _count;
                _deleter(_data);
                std::cout << "析构!" << std::endl;
                return;
            }
        }
    };
    class NullptrError : public std::exception
    {
    public:
        const char* what() const noexcept
        {
            return "空指针无法解引用!";
        }
    };
    template <class T>
    class unique_ptr
    {
    private:
        T *_data;

    public:
        unique_ptr &operator=() = delete;
        explicit unique_ptr(const unique_ptr<T> &other) = delete;
        unique_ptr(T *data)
            : _data(data)
        {
        }
        T *get() const
        {
            return _data;
        }
        T &operator*() const
        {
            if (_data == nullptr)
            {
                throw NullptrError();
            }
            return *_data;
        }
        ~unique_ptr()
        {
            delete _data;
        }
    };
}